/*
****************************************************************************************************************
*= = 版权信息：Copyright (C) 356 - 999. Ombres Industry Empiral. All rights reserved.
*= = 文件名称：Hollow.c
*= = 功能描述：工业系统电力协议定义文件
*= = 编写人员：Carlyon W. Tung
****************************************************************************************************************
*/

/*
*===============================================================================================================
*= = 声明文件
*===============================================================================================================
*/

#include <stdio.h>
#include <stdlib.h>

#ifdef __INSIGHT_OS_WINDOWS__
#include <io.h>
#include <process.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#else
#include <dirent.h>
#include <sys/time.h>
#include <unistd.h>
#endif

#ifdef USE_POLL
#include <poll.h>
#else
#ifdef __INSIGHT_OS_LINUX__
#include <sys/select.h>
#endif
#endif

#include "Hollow.h"

/*
*===============================================================================================================
*= = 预处理声明
*===============================================================================================================
*/

#ifdef HAVE_CONFIG_H
    #include <config.h>
#endif // End of HAVE_CONFIG_H

/*
*===============================================================================================================
*= = 结构体定义
*===============================================================================================================
*/

typedef struct Timer {
    uint32_t timeout;
    uint32_t counter;
    timer_func_t listener;
    pthread_t h_thread;
    syncobj_t* sync;
    int running;
} *Timer;

typedef struct SocketHeader {
    syncobj_t* sync;
    syncobj_t _sync;
#ifdef __HAVE_SSL_SUPPORT__
    SSL_CTX* ctx;
#endif
    void* data;
    int flags;
    int port;
    char addr[INET6_ADDRSTRLEN];
    listener_t listener;
} header_t;

typedef struct Connection {
    int h_socket;
    pthread_t h_thread;
    int port;
    void* parent;
    void* data;
    union {
        struct sockaddr_in peer;
        struct sockaddr_in6 peer6;
    };
    char addr[INET6_ADDRSTRLEN];
    long long int rcount;
    long long int wcount;
    time_t open_time;
#ifdef __HAVE_SSL_SUPPORT__
    SSL* ssl;
#endif
    char buffer[MAX_BUFFER_SIZE];
} *Connection;

typedef struct ServerSocket {
    header_t header;
    int count;
    int h_socket;
    pthread_t h_thread;
    Connection connections[MAX_CONNECTIONS];
#ifdef USE_POLL
    struct pollfd* fds;
#else
    fd_set* fds;
#endif
} *ServerSocket;

typedef struct ClientSocket {
    header_t header;
    int connecting;
    struct Connection connection;
} *ClientSocket;

/*
*===============================================================================================================
*= = 宏定义
*===============================================================================================================
*/

#define T_HZ        100
#define T_QUANT     (1000 / T_HZ)

/* socket flags for internal use */
#define SF_MASK     0x00FF
#define SF_CLOSING  0x0100

#define DETACH_CLIENTS

/*
*===============================================================================================================
*= = 全局变量
*===============================================================================================================
*/

/*
*===============================================================================================================
*= = 函数定义
*===============================================================================================================
*/

#ifdef __INSIGHT_OS_WINDOWS__

static WSADATA wsaData;

int sockets_init(void)
{
    WORD wVersionRequested;

    wVersionRequested = MAKEWORD(2, 2);

    return WSAStartup(wVersionRequested, &wsaData);
}

void sockets_free(void)
{
    WSACleanup();
}

unsigned int msleep(unsigned int seconds)
{
    Sleep(seconds);

    return 0;
}

#define setsockopt(a,b,c,d,e) setsockopt(a,b,c,(const void*)(d),e)
#define THREAD(t) t.p
#else
#define THREAD(t) t
#endif // End of __INSIGHT_OS_WINDOWS__

#ifdef __HAVE_SSL_SUPPORT__
static int ssl_client_init(ClientSocket c);
static int ssl_server_init(ServerSocket s);
static SSL* ssl_server_accept(ServerSocket s, int sock);
#endif

/*
 * Close and shutdown socket
 */
static void close_socket(int* h_socket)
{
    if (*h_socket < 0)
    {
        return;
    }

    int e = errno;
    int s = *h_socket;
    *h_socket = -1;
#ifdef __INSIGHT_OS_WINDOWS__
    closesocket(s);
#else
    shutdown(s, SHUT_RDWR);
    close(s);
#endif

    errno = e;
}

#ifdef __HAVE_SSL_SUPPORT__
static void close_ssl(SSL** ssl)
{
    if (*ssl)
    {
        SSL_free(*ssl);
        *ssl = NULL;
    }
}

static void close_ctx(SSL_CTX** ctx)
{
    if (*ctx)
    {
        SSL_CTX_free(*ctx);
        *ctx = NULL;
    }
}
#endif

/*
 * Accept new non-blocking server connection
 * Return connection if success, otherwise NULL
 */
static Connection accept_connection(const server_t server)
{
    int	sock;
    unsigned long int flags;
    struct sockaddr_in client;
    struct sockaddr_in6 client6;
    socklen_t len;
    Connection c;
    ServerSocket s = (ServerSocket)server;
    event_t e;
    e.server = s;
    if (s->header.flags & SF_IPV6)
    {
        len = sizeof(client6);
        sock = accept(s->h_socket, (struct sockaddr*)&client6, &len);
    }
    else
    {
        len = sizeof(client);
        sock = accept(s->h_socket, (struct sockaddr*)&client, &len);
    }
    if (sock < 0)
    {
        switch (errno)
        {
        case EAGAIN:
#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK
        case EWOULDBLOCK:
#endif
            break;
        default:
            if (s->header.listener != NULL)
            {
                e.type = SE_ERROR;
                e.error = errno;
                s->header.listener(&e);
            }
        }
        return NULL;
    }
#ifdef __INSIGHT_OS_WINDOWS__
    flags = 1;
    if (ioctlsocket(sock, FIONBIO, &flags) != 0)
    {
        close_socket(&sock);
        return NULL;
    }
#else
    flags = fcntl(sock, F_GETFL, 0);
    if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
    {
        close_socket(&sock);
        return NULL;
    }
#endif
    c = calloc(1, sizeof(struct Connection));
    if (c == NULL) {
        close_socket(&sock);
        return NULL;
    }
    c->h_socket = sock;
    c->parent = s;
    if (s->header.flags & SF_IPV6)
    {
        c->peer6 = client6;
        inet_ntop(AF_INET6, (char*)&(client6.sin6_addr), (char*)&(c->addr), INET6_ADDRSTRLEN);
    }
    else
    {
        c->peer = client;
        inet_ntop(AF_INET, (char*)&(client.sin_addr), (char*)&(c->addr), INET_ADDRSTRLEN);
    }
    e.connection = c;
    errno = s->count == MAX_CONNECTIONS ? ECONNABORTED : 0;
    if (errno == 0 && s->header.listener != NULL)
    {
        e.type = SE_ACCEPT;
        e.error = 0;
        s->header.listener(&e);
        errno = e.error;
    }
    if (errno != 0)
    {
        if (s->header.listener != NULL)
        {
            e.type = SE_ERROR;
            e.error = errno;
            s->header.listener(&e);
            e.type = SE_DISCONNECT;
            s->header.listener(&e);
        }
        close_socket(&c->h_socket);
        free(c);
        return NULL;
    }
    else
    {
        c->open_time = time(NULL);
        s->connections[s->count++] = c;
#ifdef USE_POLL
        s->fds[s->count] = c->h_socket;
#endif
        if (s->header.listener != NULL)
        {
            e.type = SE_CONNECT;
            s->header.listener(&e);
        }
        return c;
    }
}

/* Read from non-blocking connection up to MAX_BUFFER_SIZE bytes
 * or until error occured
 * Store number of read bytes to result (if it is not NULL)
 * Return 0 if success, otherwise -1
 */
static int connection_read(const connection_t connection, int* result)
{
    if (connection == NULL)
    {
        errno = EINVAL;
        return -1;
    }
    Connection c = (Connection)connection;
    if (c->h_socket < 0)
    {
        errno = EPIPE;
        return -1;
    }
#ifdef __HAVE_SSL_SUPPORT__
    if (c->ssl)
    {
        int read_result = SSL_read(c->ssl, c->buffer, MAX_BUFFER_SIZE);
        if (read_result < 0) return -1;
        if (result != NULL) *result = read_result;
        c->rcount += read_result;
        return 0;
    }
#endif
    int bytes_read = 0;
    while (c->h_socket > 0)
    {
        int len = MAX_BUFFER_SIZE - bytes_read;
        int read_result = recv(c->h_socket, c->buffer + bytes_read, len, MSG_NOSIGNAL);
        if (read_result < 0)
        {
            switch (errno)
            {
            case EAGAIN:
#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK
            case EWOULDBLOCK:
#endif
                if (result != NULL) *result = bytes_read;
                return 0;
            case EINTR:
                continue;
            }
            if (result != NULL) *result = bytes_read;
            c->rcount += bytes_read;
            return -1;
        }
        else
        {
            bytes_read += read_result;
            if (read_result == 0) break;
        }
        if (bytes_read == MAX_BUFFER_SIZE) break;
    }
    if (result != NULL) *result = bytes_read;
    c->rcount += bytes_read;
    return 0;
}

/* Close non-blocking server connection
 * Return 0 if success, otherwise (it seems impossible?) -1
 */
static int server_close_connection(const connection_t connection)
{
    if (connection == NULL) return 0;
    Connection c = (Connection)connection;
    int i, j;
    event_t e;
    ServerSocket s = (ServerSocket)c->parent;
    for (i = 0; i < s->count; i++) if (s->connections[i] == c)
    {
        if (s->header.listener != NULL)
        {
            e.server = s;
            e.connection = c;
            e.type = SE_DISCONNECT;
            s->header.listener(&e);
        }
        free(c);
#ifdef __HAVE_SSL_SUPPORT__
        if (c->ssl) close_ssl(&c->ssl);
#endif
        close_socket(&c->h_socket);
        for (j = i + 1; j < s->count - 1; j++)
        {
            s->connections[j - 1] = s->connections[j];
#ifdef USE_POLL
            s->fds[j] = s->fds[j + 1];
#endif
        }
        s->count--;
        return 0;
    }
    return -1;
}

/* Creates new server
 * addr:    bind IP address
 * port:    listening port
 * flags:
 *          SF_IPV6         use IPv6 socket (by default IPv4)
 *          SF_NONBLOCKING  use non-blocking socket (by default uses blocking)
 *          SF_SSL          use SSL (if enabled); currently conflicted with SF_NONBLOCKING
 * sync:    object for synchronization (if NULL, new object will be created)
 * Return   server ID if success, otherwise NULL
 */
server_t server_new(const char* addr, const int port, const int flags, syncobj_t* sync)
{
    if (port < 1 || port > 65535)
    {
        errno = EINVAL;
        return NULL;
    }
#ifdef __HAVE_SSL_SUPPORT__
    if ((flags & (SF_NONBLOCKING | SF_SSL)) == (SF_NONBLOCKING | SF_SSL))
    {
        errno = EINVAL;
        return NULL;
    }
#endif    
    ServerSocket s = (ServerSocket)calloc(1, sizeof(struct ServerSocket));
    if (s == NULL) return NULL;
    s->header.flags = flags & SF_MASK;
    if (sync != NULL)
    {
        s->header.sync = sync;
    }
    else
    {
        if (sync_init(&s->header._sync) < 0)
        {
            free(s);
            return NULL;
        }
        s->header.sync = &s->header._sync;
    }
#ifdef USE_POLL
    s->fds = (struct pollfd*)calloc(MAX_CONNECTIONS, sizeof(struct pollfd));
#else
    s->fds = (fd_set*)calloc(2, sizeof(fd_set));
#endif
    if (addr != NULL) strncpy(s->header.addr, addr, sizeof(s->header.addr));
    s->header.port = port;
    s->h_socket = -1;
    return s;
}

/*
 * Delete server
 */
void server_free(const server_t server)
{
    if (server == NULL) return;
    server_stop(server);
    ServerSocket s = (ServerSocket)server;
    if (s->header.sync == &s->header._sync)
        sync_free(&s->header._sync);
    if (s->fds)
        free(s->fds);
    free(s);
}

/*
 * Non-blocking server thread
 */
static void* server_func_nb(void* arg)
{
    ServerSocket s = (ServerSocket)arg;
    if (s == NULL || s->h_socket < 0) return NULL;
    int i, nbytes, totalbytes;
    Connection c;
    event_t e;
    e.server = s;
#ifdef USE_POLL
    s->fds[0].fd = s->h_socket;
    s->fds[0].events = POLLIN;
    int timeout = 1000;
#else
    struct timeval tv = { 1, 0 };
#endif
    while (s->h_socket > 0)
    {
#ifdef USE_POLL
        res = poll(s->fds, s->count + 1, timeout);
#else
        FD_ZERO(&s->fds[0]);
        FD_ZERO(&s->fds[1]);
        FD_SET(s->h_socket, &s->fds[0]);
        FD_SET(s->h_socket, &s->fds[1]);
        int fd, max_fd = s->h_socket;
        for (i = 0; i < s->count; i++)
        {
            fd = s->connections[i]->h_socket;
            FD_SET(fd, &s->fds[0]);
            FD_SET(fd, &s->fds[1]);
            if (fd > max_fd)
                max_fd = fd;
        }
        int res = select(max_fd + 1, &s->fds[0], NULL, &s->fds[1], &tv);
#endif
        if (res < 0)
        {
            switch (errno)
            {
            case EAGAIN:
#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK
            case EWOULDBLOCK:
#endif
                msleep(1);
                continue;
            case EINTR:
                continue;
            default: // EINVAL ?
                //perror("Server error:");
                server_stop(s);
                return 0;
            }
        }
        if (res == 0)
        {
            //printf("Timeout\n");
            continue;
        }
        if (s->h_socket < 0) break;
        server_lock(&s->header.sync);
        for (i = 0; i <= s->count && s->h_socket > 0; i++)
        {
#ifdef USE_POLL
            if (s->fds[i].revents == 0)
                continue;
            if (s->fds[i].revents != POLLIN)
            {
                if (i > 0)
                {
                    server_close_connection(s->connections[--i]);
                    continue;
                }
                else
                {
                    // ???
                    server_stop(s);
                    break;
                }
            }
#else
            if (i == 0) {
                if (FD_ISSET(s->h_socket, &s->fds[1]))
                {
                    // ???
                    server_stop(s);
                    break;
                }
                if (!FD_ISSET(s->h_socket, &s->fds[0])) continue;
            }
            else
            {
                fd = s->connections[i - 1]->h_socket;
                if (FD_ISSET(fd, &s->fds[1]))
                {
                    server_close_connection(s->connections[--i]);
                    continue;
                }
                if (!FD_ISSET(fd, &s->fds[0])) continue;
            }
#endif
            if (i == 0)
            {
                /* Listening descriptor is readable */
                do {
                    if ((c = accept_connection(s)) == NULL)
                    {
                        // Process errors
                        break;
                    }
                } while (c != NULL);
            }
            else
            {
                /* existing connection must be readable */
                c = s->connections[i - 1];
                e.connection = c;
                e.type = SE_READ;
                e.io.buffer = (char*)&c->buffer;
                totalbytes = 0;
                while (s->h_socket > 0 && (res = connection_read(c, &nbytes)) == 0 && nbytes > 0)
                {
                    if (s->header.listener != NULL)
                    {
                        e.io.len = nbytes;
                        s->header.listener(&e);
                    }
                    totalbytes += nbytes;
                }
                if (res < 0 || totalbytes == 0)
                {
                    server_close_connection(s->connections[--i]);
                    break;
                }
            }
        }
        server_unlock(&s->header.sync);
    }
    while (s->count > 0)
    {
        server_close_connection(s->connections[s->count - 1]);
    }
    return NULL;
}

/*
 *  Blocking server connections thread
 */
static void* connection_func(void* arg)
{
    Connection c = (Connection)arg;
    ServerSocket s = (ServerSocket)(c->parent);
    event_t e;
    e.server = s;
    e.connection = c;
    while (c->h_socket > 0)
    {
        int nbytes;
#ifdef __HAVE_SSL_SUPPORT__
        if (c->ssl)
            nbytes = SSL_read(c->ssl, c->buffer, MAX_BUFFER_SIZE);
        else
            nbytes = recv(c->h_socket, c->buffer, MAX_BUFFER_SIZE, MSG_NOSIGNAL);
#else
        nbytes = recv(c->h_socket, c->buffer, MAX_BUFFER_SIZE, MSG_NOSIGNAL);
#endif
        if (nbytes <= 0) {
            if (nbytes == 0) break;
            if (errno == EINTR) continue;
            if (s->header.listener != NULL) {
                e.type = SE_ERROR;
                e.error = errno;
                s->header.listener(&e);
            }
            break;
        }
        sync_lock(s->header.sync);
        if (c->h_socket > 0) {
            c->rcount += nbytes;
            if (s->header.listener != NULL) {
                e.type = SE_READ;
                e.io.buffer = c->buffer;
                e.io.len = nbytes;
                s->header.listener(&e);
            }
        }
        sync_unlock(s->header.sync);
    }

    sync_lock(s->header.sync);
    int i;
    for (i = 0; i < s->count; i++) if (s->connections[i] == c)
    {
        if (s->header.listener != NULL)
        {
            e.type = SE_DISCONNECT;
            e.connection = c;
            s->header.listener(&e);
        }
        for (++i; i < s->count; i++) s->connections[i - 1] = s->connections[i];
        s->count--;
        break;
    }
#ifdef __HAVE_SSL_SUPPORT__
    if (c->ssl)
    {
        close_ssl(&c->ssl);
        ERR_remove_state(0);
        CRYPTO_cleanup_all_ex_data();
    }
#endif
    close_socket(&c->h_socket);
    free(c);
    sync_unlock(s->header.sync);
    //pthread_exit(NULL);
    return NULL;
}

/*
 *  Blocking server thread
 */
static void* server_func(void* arg)
{
    int	i, sock;
    struct sockaddr_in client;
    struct sockaddr_in6 client6;
    socklen_t len;
    Connection c;
    event_t e;
#ifdef __HAVE_SSL_SUPPORT__
    SSL* ssl;
#endif    
    ServerSocket s = (ServerSocket)arg;
    e.server = s;
    while (s->h_socket > 0) {
        if (s->header.flags & SF_IPV6) {
            len = sizeof(client6);
            sock = accept(s->h_socket, (struct sockaddr*)&client6, &len);
        }
        else {
            len = sizeof(client);
            sock = accept(s->h_socket, (struct sockaddr*)&client, &len);
        }
        if (sock < 0)
        {
            if (errno == EINTR) continue;
            if (s->header.listener != NULL && (s->header.flags & SF_CLOSING) == 0)
            {
                e.type = SE_ERROR;
                e.error = errno;
                e.connection = NULL;
                sync_lock(s->header.sync);
                s->header.listener(&e);
                sync_unlock(s->header.sync);
            }
            break;
        }
#ifdef __HAVE_SSL_SUPPORT__
        if ((s->header.flags & SF_SSL) != 0 && (ssl = ssl_server_accept(s, sock)) == NULL)
        {
            if (s->header.listener != NULL && (s->header.flags & SF_CLOSING) == 0)
            {
                e.type = SE_ERROR;
                e.error = errno;
                e.connection = NULL;
                sync_lock(s->header.sync);
                s->header.listener(&e);
                sync_unlock(s->header.sync);
            }
            close_socket(&sock);
            continue;
        }
#endif
        c = malloc(sizeof(struct Connection));
        if (c == NULL)
        {
            e.type = SE_ERROR;
            e.error = errno;
            e.connection = NULL;
            sync_lock(s->header.sync);
            s->header.listener(&e);
            sync_unlock(s->header.sync);
#ifdef __HAVE_SSL_SUPPORT__
            if (s->header.flags & SF_SSL) SSL_free(ssl);
#endif
            close_socket(&sock);
            continue;
        }
        memset(c, 0, sizeof(struct Connection));
        c->h_socket = sock;
        c->parent = s;
        c->port = s->header.port;
        if (s->header.flags & SF_IPV6) {
            c->peer6 = client6;
            inet_ntop(AF_INET6, (char*)&(client6.sin6_addr), (char*)&(c->addr), INET6_ADDRSTRLEN);
        }
        else {
            c->peer = client;
            inet_ntop(AF_INET, (char*)&(client.sin_addr), (char*)&(c->addr), INET_ADDRSTRLEN);
        }
#ifdef __HAVE_SSL_SUPPORT__
        if (s->header.flags & SF_SSL) c->ssl = ssl;
#endif
        e.connection = c;
        errno = s->count == MAX_CONNECTIONS ? ECONNABORTED : 0;
        if (errno == 0 && s->header.listener != NULL)
        {
            e.type = SE_ACCEPT;
            e.error = 0;
            sync_lock(s->header.sync);
            s->header.listener(&e);
            errno = e.error;
            sync_unlock(s->header.sync);
        }
        if (errno != 0)
        {
            if (s->header.listener != NULL)
            {
                e.type = SE_ERROR;
                e.error = errno;
                sync_lock(s->header.sync);
                s->header.listener(&e);
                e.type = SE_DISCONNECT;
                s->header.listener(&e);
                sync_unlock(s->header.sync);
            }
#ifdef __HAVE_SSL_SUPPORT__
            if (s->header.flags & SF_SSL) SSL_free(ssl);
#endif
            close_socket(&sock);
            free(c);
        }
        else
        {
            sync_lock(s->header.sync);
            if (pthread_create(&c->h_thread, NULL, connection_func, c) != 0)
            {
                if (s->header.listener != NULL)
                {
                    e.type = SE_ERROR;
                    e.error = errno;
                    s->header.listener(&e);
                    e.type = SE_DISCONNECT;
                    s->header.listener(&e);
                }
#ifdef __HAVE_SSL_SUPPORT__
                if (s->header.flags & SF_SSL) SSL_free(ssl);
#endif
                close_socket(&sock);
                free(c);
            }
            else {
                c->open_time = time(NULL);
                s->connections[s->count++] = c;
                if (s->header.listener != NULL)
                {
                    e.type = SE_CONNECT;
                    s->header.listener(&e);
                }
                pthread_detach(c->h_thread);
            }
            sync_unlock(s->header.sync);
        }
    }
    sync_lock(s->header.sync);
    close_socket(&s->h_socket);
    if (s->header.listener != NULL)
    {
        e.type = SE_CLOSE;
        s->header.listener(&e);
    }
    for (i = 0; i < s->count; i++)
    {
        c = s->connections[i];
        close_socket(&c->h_socket);
    }
    sync_unlock(s->header.sync);
    while (s->count > 0)
    {
        msleep(10);
    }
#ifdef __HAVE_SSL_SUPPORT__
    if (s->header.ctx) {
        close_ctx(&s->header.ctx);
        ERR_remove_state(0);
        CRYPTO_cleanup_all_ex_data();
    }
#endif
    return NULL;
}

/*
 * Start server listening
 */
int server_start(const server_t server)
{
    if (server == NULL)
    {
        errno = EINVAL;
        return -1;
    }
    ServerSocket s = (ServerSocket)server;
    struct sockaddr_in sin;
    struct sockaddr_in6 sin6;
    int optval;
    unsigned long int flags;
    event_t e;
    e.server = s;
    s->header.flags &= ~SF_CLOSING;
    if (s->h_socket > 0) {
        errno = EALREADY;
        return -1;
    }

    if (s->header.flags & SF_IPV6)
    {
        memset(&sin6, 0, sizeof(sin6));
        sin6.sin6_port = htons(s->header.port);
        sin6.sin6_family = AF_INET6;
        if (s->header.addr[0] == 0)
            sin6.sin6_addr = in6addr_any;
        else {
            int i = inet_pton(AF_INET6, s->header.addr, &sin6.sin6_addr);
            if (i != 1) {
                if (i == 0) errno = EINVAL;
                return -1;
            }
        }
        if ((s->h_socket = socket(PF_INET6, SOCK_STREAM, 0)) < 0) return -1;
        optval = 1;
        setsockopt(s->h_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
        if (s->header.flags & SF_NONBLOCKING) {
#ifdef __INSIGHT_OS_WINDOWS__
            flags = 1;
            if (ioctlsocket(s->h_socket, FIONBIO, &flags) != 0)
            {
                close_socket(&s->h_socket);
                return -1;
            }
#else
            flags = fcntl(s->h_socket, F_GETFL, 0);
            if (fcntl(s->h_socket, F_SETFL, flags | O_NONBLOCK) < 0)
            {
                close_socket(&s->h_socket);
                return -1;
            }
#endif
        }

        if (bind(s->h_socket, (struct sockaddr*)&sin6, sizeof(struct sockaddr_in6)) < 0)
        {
            close_socket(&s->h_socket);
            return -1;
        }
    }
    else
    {
        memset(&sin, 0, sizeof(sin));
        sin.sin_port = htons(s->header.port);
        sin.sin_family = AF_INET;
        if (s->header.addr[0] == 0)
            sin.sin_addr.s_addr = htonl(INADDR_ANY);
        else {
            int i = inet_pton(AF_INET, s->header.addr, &sin.sin_addr.s_addr);
            if (i != 1) {
                if (i == 0) errno = EINVAL;
                return -1;
            }
        }
        if ((s->h_socket = socket(PF_INET, SOCK_STREAM, 0)) < 0) return -1;
        optval = 1;
        setsockopt(s->h_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int));
        if (s->header.flags & SF_NONBLOCKING) {
#ifdef __INSIGHT_OS_WINDOWS__
            flags = 1;
            if (ioctlsocket(s->h_socket, FIONBIO, &flags) != 0)
            {
                close_socket(&s->h_socket);
                return -1;
            }
#else
            flags = fcntl(s->h_socket, F_GETFL, 0);
            if (fcntl(s->h_socket, F_SETFL, flags | O_NONBLOCK) < 0)
            {
                close_socket(&s->h_socket);
                return -1;
            }
#endif
        }
        if (bind(s->h_socket, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)) < 0)
        {
            close_socket(&s->h_socket);
            return -1;
        }
    }

    if (listen(s->h_socket, MAX_CONNECTIONS) < 0)
    {
        close_socket(&s->h_socket);
        return -1;
    }
#ifdef __HAVE_SSL_SUPPORT__
    if (s->header.flags & SF_SSL)
    {
        if (ssl_server_init(s) < 0)
        {
            close_socket(&s->h_socket);
            return -1;
        }
    }
#endif
    if (s->header.listener != NULL)
    {
        e.type = SE_LISTEN;
        s->header.listener(&e);
    }
    if (pthread_create(&s->h_thread, NULL, (s->header.flags & SF_NONBLOCKING) ? server_func_nb : server_func, s) < 0)
    {
        if (s->header.listener != NULL)
        {
            e.type = SE_CLOSE;
            s->header.listener(&e);
        }
#ifdef __HAVE_SSL_SUPPORT__
        if (s->header.ctx) close_ctx(&s->header.ctx);
#endif
        close_socket(&s->h_socket);
        return -1;
    }
    return 0;
}


/*
 * Stop server listening
 */
void server_stop(const server_t server)
{
    if (server == NULL) return;
    ServerSocket s = (ServerSocket)server;
    s->header.flags |= SF_CLOSING;
    close_socket(&s->h_socket);
}

/*
 * Return 1 if server is listening, otherwise 0
 */
int server_started(const server_t server)
{
    return (server == NULL) ? 0 : ((ServerSocket)server)->h_socket > 0 ? 1 : 0;
}

/*
 * Lock synchronization mutex
 */
void server_lock(const server_t server)
{
    if (server == NULL) return;
    sync_lock(((ServerSocket)server)->header.sync);
}

/*
 * Unlock synchronization mutex
 */
void server_unlock(const server_t server)
{
    if (server == NULL) return;
    sync_unlock(((ServerSocket)server)->header.sync);
}

/*
 * Return connection by index, or NULL on error
 */
connection_t server_get_connection(const server_t server, const int index)
{
    return (server == NULL || index < 0 || ((ServerSocket)server)->count <= index) ? NULL : ((ServerSocket)server)->connections[index];
}

/*
 * Return index of connection, or -1 if there is not such connection
 */
int server_get_index(const server_t server, const connection_t connection)
{
    int i;
    ServerSocket s = (ServerSocket)server;
    for (i = 0; i < s->count; i++)
        if (s->connections[i] == connection)
            return i;
    return -1;
}

/*
 * Waiting while server thread running
 */
void server_wait(const server_t server)
{
    if (server == NULL) return;
    ServerSocket s = (ServerSocket)server;
    if (THREAD(s->h_thread)) {
        pthread_join(s->h_thread, NULL);
        THREAD(s->h_thread) = 0;
    }
}

/*
 * Set server listener to proceess events
 */
void server_set_listener(const server_t server, listener_t listener)
{
    if (server == NULL) return;
    ServerSocket s = (ServerSocket)server;
    s->header.listener = listener;
}

/*
 * Return connections count
 */
int server_get_count(const server_t server)
{
    return (server == NULL) ? 0 : ((ServerSocket)server)->count;
}

/*
 * Return user data ptr
 */
void* server_get_data(const server_t server)
{
    return (server == NULL) ? NULL : ((ServerSocket)server)->header.data;
}

/*
 * Set user data ptr
 */
void server_set_data(const server_t server, void* data)
{
    if (server != NULL) ((ServerSocket)server)->header.data = data;
}

/*
 * Get listening socket handle
 */
int server_get_handle(const server_t server)
{
    return (server == NULL) ? -1 : ((ServerSocket)server)->h_socket;
}

/*
 * Get server info: bind address, port, ipv6 or ipv4 protocol
 */
int server_get_info(const server_t server, char* addr, int* port, int* ipv6)
{
    if (server == NULL) return -1;
    ServerSocket s = (ServerSocket)server;
    if (addr != NULL) strcpy(addr, s->header.addr);
    if (port != NULL) *port = s->header.port;
    if (ipv6 != NULL) *ipv6 = (s->header.flags & SF_IPV6) ? 1 : 0;
    return 0;
}

/*
 * Close selected connection
 */
void connection_close(connection_t connection)
{
    if (connection == NULL) return;
    Connection c = (Connection)connection;
    close_socket(&c->h_socket);
}

/*
 * Write to selected connection
 * Set var pointed by result to number of written bytes
 * Return 0 if all bytes written, otherwise -1
 */
int connection_write(const connection_t connection, const void* buffer, const size_t len, int* result)
{
    if (connection == NULL || buffer == NULL)
    {
        errno = EINVAL;
        return -1;
    }
    if (len == 0)
    {
        if (result != NULL) *result = 0;
        return 0;
    }
    Connection c = (Connection)connection;
    if (c->h_socket < 0)
    {
        errno = EPIPE;
        return -1;
    }
#ifdef __HAVE_SSL_SUPPORT__
    if (c->ssl) {
        int write_result = SSL_write(c->ssl, buffer, len);
        if (write_result < 0) return -1;
        if (result != NULL) *result = write_result;
        c->wcount += write_result;
        return 0;
    }
#endif
    int bytes_written = 0;
    while (c->h_socket > 0 && len > bytes_written)
    {
        int write_result = send(c->h_socket, (char*)buffer + bytes_written, len - bytes_written, MSG_NOSIGNAL);
        if (write_result < 0)
        {
            switch (errno)
            {
            case EAGAIN:
#if defined(EWOULDBLOCK) && EAGAIN != EWOULDBLOCK
            case EWOULDBLOCK:
#endif
                msleep(1);
                continue;
            case EINTR:
                continue;
            }
            if (result != NULL) *result = bytes_written;
            c->wcount += bytes_written;
            return -1;
        }
        else {
            bytes_written += write_result;
            if (write_result == 0) break;
        }
    }
    if (result != NULL) *result = bytes_written;
    c->wcount += bytes_written;
    return 0;
}

/*
 * Return connection user data ptr
 */
void* connection_get_data(const connection_t connection)
{
    return (connection == NULL) ? NULL : ((Connection)connection)->data;
}

/*
 * Set connection user data ptr
 */
void connection_set_data(const connection_t connection, void* data)
{
    if (connection != NULL) ((Connection)connection)->data = data;
}

/*
 * Return connection received bytes number
 */
long long int connection_received(const connection_t connection)
{
    return (connection == NULL) ? 0 : ((Connection)connection)->rcount;
}

/*
 * Return connection transmitted bytes number
 */
long long int connection_transmitted(const connection_t connection)
{
    return (connection == NULL) ? 0 : ((Connection)connection)->wcount;
}

/*
 * Return connection peer if IPv4 used
 */
struct sockaddr_in* connection_get_peer(const connection_t connection)
{
    return (connection == NULL) ? NULL : &((Connection)connection)->peer;
};

/*
 * Return connection peer if IPv6 used
 */
struct sockaddr_in6* connection_get_peer6(const connection_t connection)
{
    return (connection == NULL) ? NULL : &((Connection)connection)->peer6;
};

/*
 * Return connection remote IP address
 */
const char* connection_get_address(const connection_t connection)
{
    return (connection == NULL) ? NULL : (char*)(&((Connection)connection)->addr);
};

/*
 * Return connection local port
 */
unsigned short connection_get_lport(const connection_t connection)
{
    return (connection == NULL) ? 0 : ((Connection)connection)->port;
};

/*
 * Return connection remote port
 */
unsigned short connection_get_rport(const connection_t connection)
{
    return (connection == NULL) ? 0 : ntohs(((Connection)connection)->peer.sin_port);
};

/*
 * Return server associated with selected connection
 */
server_t connection_get_server(const connection_t connection)
{
    return (connection == NULL) ? NULL : ((Connection)connection)->parent;
}

/*
 * Return duration of connection
 */
time_t connection_get_time(const connection_t connection)
{
    return (connection == NULL) ? 0 : ((Connection)connection)->open_time;
}
/* Create new client
 * addr:    server IP address
 * port:    connection port
 * flags:
 *          SF_IPV6         use ipv6 socket (by default ipv4)
 *          SF_NONBLOCKING  use non-blocking socket (by default blocking)
 *          SF_SSL          use SSL (if enabled); currently conflicted with SF_NONBLOCKING
 * sync:    object for synchronization (if NULL, creates new)
 * Return   client ID if success, otherwise NULL
 */
client_t client_new(const char* addr, const int port, const int flags, syncobj_t* sync)
{
    if (addr == NULL || port < 1 || port > 65535)
    {
        errno = EINVAL;
        return NULL;
    }
#ifdef __HAVE_SSL_SUPPORT__
    if ((flags & (SF_NONBLOCKING | SF_SSL)) == (SF_NONBLOCKING | SF_SSL))
    {
        errno = EINVAL;
        return NULL;
    }
#endif    
    ClientSocket c = (ClientSocket)calloc(1, sizeof(struct ClientSocket));
    if (c == NULL) return NULL;
    c->header.flags = flags & SF_MASK;
    if (sync != NULL) {
        c->header.sync = sync;
    }
    else
    {
        if (sync_init(&c->header._sync) < 0)
        {
            free(c);
            return NULL;
        }
        c->header.sync = &c->header._sync;
    }
    c->connection.h_socket = -1;
    c->header.port = port;
    strncpy(c->header.addr, addr, sizeof(c->header.addr));
    return c;
}

/*
 * Delete client
 */
void client_free(const client_t client)
{
    if (client == NULL) return;
    ClientSocket c = (ClientSocket)client;
    client_close(c);
    //client_wait(c);
    if (c->header.sync == &c->header._sync)
        sync_free(&c->header._sync);
    free(c);
}

/*
 *  Client thread
 */
static void* client_func(void* arg)
{
    ClientSocket client = (ClientSocket)arg;
    Connection c = &client->connection;
    event_t e;
    e.client = client;
    e.connection = c;
    while (c->h_socket > 0) {
        int nbytes;
#ifdef __HAVE_SSL_SUPPORT__
        if (c->ssl)
            nbytes = SSL_read(c->ssl, c->buffer, MAX_BUFFER_SIZE);
        else
            nbytes = recv(c->h_socket, c->buffer, MAX_BUFFER_SIZE, MSG_NOSIGNAL);
#else
        nbytes = recv(c->h_socket, c->buffer, MAX_BUFFER_SIZE, MSG_NOSIGNAL);
#endif
        if (nbytes <= 0)
        {
            if (nbytes == 0) break;
            if (errno == EINTR) continue;
            if (client->header.listener != NULL)
            {
                e.type = SE_ERROR;
                e.error = errno;
                sync_lock(client->header.sync);
                client->header.listener(&e);
                sync_unlock(client->header.sync);
            }
            break;
        }
        c->rcount += nbytes;
        if (c->h_socket > 0)
        {
            if (client->header.listener != NULL)
            {
                e.type = SE_READ;
                e.io.buffer = c->buffer;
                e.io.len = nbytes;
                sync_lock(client->header.sync);
                client->header.listener(&e);
                sync_unlock(client->header.sync);
            }
        }
    }
    sync_lock(client->header.sync);
    if (client->header.listener != NULL)
    {
        e.type = SE_DISCONNECT;
        client->header.listener(&e);
    }
#ifdef __HAVE_SSL_SUPPORT__
    if (c->ssl) close_ssl(&c->ssl);
#endif
    close_socket(&c->h_socket);
#ifdef __HAVE_SSL_SUPPORT__
    if (client->header.ctx) {
        close_ctx(&client->header.ctx);
        ERR_remove_state(0);
        CRYPTO_cleanup_all_ex_data();
    }
#endif
    THREAD(client->connection.h_thread) = 0;
    sync_unlock(client->header.sync);
    //pthread_exit(NULL);
    return NULL;
}

struct addrinfo hints = { AI_NUMERICSERV, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL };

/*
 * Connect to server
 * Return 0 if connection established, otherwise -1
 */
int client_open(const client_t client)
{
    int h_socket;
    struct addrinfo* ai;
    ClientSocket c = (ClientSocket)client;
    struct sockaddr_in sa;
    struct sockaddr_in6 sa6;
    socklen_t sl;
    char svc[10];
    event_t e;
    int i;

    if (client == NULL)
    {
        errno = EINVAL;
        return -1;
    }

    if (c->connection.h_socket > 0)
    {
        return 0;
    }

    if (c->header.flags & SF_IPV6)
    {
        hints.ai_family = AF_INET6;
        if ((h_socket = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
        {
            return -1;
        }
    }
    else
    {
        if ((h_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            return -1;
        }
    }

    sprintf(svc, "%d", c->header.port);
    if ((errno = getaddrinfo(c->header.addr, svc, &hints, &ai)) != 0)
    {
        close(h_socket);
        h_socket = -1;
        return -1;
    }

    i = connect(h_socket, ai->ai_addr, ai->ai_addrlen);
    freeaddrinfo(ai);
    if (i < 0)
    {
        close(h_socket);
        h_socket = -1;
        return -1;
    }

    if (c->header.flags & SF_IPV6)
    {
        sl = sizeof(sa6);
        getsockname(h_socket, (struct sockaddr*)&sa6, &sl);
        c->connection.peer6 = sa6;
        getnameinfo((struct sockaddr*)&sa6, sizeof(sa6),
            c->connection.addr, sizeof(c->connection.addr), NULL,
            0, NI_NUMERICHOST | NI_NUMERICSERV);
    }
    else
    {
        sl = sizeof(sa);
        getsockname(h_socket, (struct sockaddr*)&sa, &sl);
        c->connection.peer = sa;
        strcpy(c->connection.addr, inet_ntoa(sa.sin_addr));
    }

    c->connection.parent = client;
    c->connection.port = c->header.port;
    c->connection.open_time = time(NULL);
    c->connection.h_socket = h_socket;
#ifdef __HAVE_SSL_SUPPORT__
    if (c->header.flags & SF_SSL)
    {
        if (ssl_client_init(c) < 0) return -1;
    }
#endif
    if (c->connecting == 0)
    {
        if (pthread_create(&c->connection.h_thread, NULL, client_func, c) != 0)
        {

#ifdef __HAVE_SSL_SUPPORT__
            if (c->connection.ssl) close_ssl(&c->connection.ssl);
#endif

            close_socket(&c->connection.h_socket);

#ifdef __HAVE_SSL_SUPPORT__
            if (c->header.ctx) close_ctx(&c->header.ctx);
#endif
            return -1;
        }
#ifdef DETACH_CLIENTS
        pthread_detach(c->connection.h_thread);
#endif
    }
    if (c->header.listener != NULL)
    {
        e.type = SE_CONNECT;
        e.client = c;
        e.connection = &c->connection;
        c->header.listener(&e);
    }
    return 0;
}

/*
 *  Client waiting for establish connection thread (for deferred connection)
 */
static void* client_connect_thread(void* arg)
{
    event_t e;
    ClientSocket c = (ClientSocket)arg;
    int res = client_open(c);
    c->connecting = 0;
    if (res < 0) {
        if (c->header.listener != NULL)
        {
            e.type = SE_ERROR;
            e.client = c;
            e.connection = &c->connection;
            e.error = errno;
            sync_lock(c->header.sync);
            c->header.listener(&e);
            sync_unlock(c->header.sync);
        }
        return NULL;
    }
    return client_func(c);
}

/*
 * Connect to server, do not wait for connection established
 * Return 0 if success, otherwise -1
 */
int client_try_open(const client_t client)
{
    if (client == NULL) {
        errno = EINVAL;
        return -1;
    }
    ClientSocket c = (ClientSocket)client;
    c->connecting = 1;
    if (pthread_create(&c->connection.h_thread, NULL, client_connect_thread, c) != 0)
        return -1;
    pthread_detach(c->connection.h_thread);
    return 0;
}

/*
 * Disconnects from server
 */
void client_close(const client_t client)
{
    if (client == NULL) return;
    ClientSocket c = (ClientSocket)client;
#ifdef __HAVE_SSL_SUPPORT__
    //if (c->connection.ssl) close_ssl(&c->connection.ssl);
#endif
    close_socket(&c->connection.h_socket);
#ifdef __HAVE_SSL_SUPPORT__
    //if (c->header.ctx) close_ctx(&c->header.ctx);
#endif
}

/*
 * Return 1 if connection established, otherwise 0
 */
int client_connected(const client_t client)
{
    return (client == NULL) ? 0 : (((ClientSocket)client)->connection.h_socket > 0 ? 1 : 0);
}

/*
 * Write data to server
 * Set var pointed by result to number of written bytes
 * Return 0 if all bytes written, otherwise -1
 */
int client_write(const client_t client, const void* buffer, const size_t len, int* result)
{
    if (client == NULL || buffer == NULL)
    {
        errno = EINVAL;
        return -1;
    }
    return connection_write(&((ClientSocket)client)->connection, buffer, len, result);
}

/*
 * Return connection associated with client
 */
connection_t client_get_connection(const client_t client)
{
    return client == NULL ? NULL : &((ClientSocket)client)->connection;
}

/*
 * Set client listener to process events
 */
void client_set_listener(const client_t client, listener_t listener)
{
    if (client == NULL) return;
    ClientSocket c = (ClientSocket)client;
    c->header.listener = listener;
}

/*
 * Wait while client thread running
 */
void client_wait(const client_t client)
{
    if (client == NULL) return;
    ClientSocket c = (ClientSocket)client;
    if (THREAD(c->connection.h_thread))
    {
#ifdef DETACH_CLIENTS
        int i;
        for (i = 0; i < 100; i++)
        {
            if (THREAD(c->connection.h_thread) == 0) return;
            msleep(10);
        }
#else
        pthread_join(c->connection.h_thread, NULL);
#endif
        THREAD(c->connection.h_thread) = 0;
    }
}

/*
 * Lock synchronization mutex
 */
void client_lock(const client_t client)
{
    if (client == NULL) return;
    sync_lock(((ClientSocket)client)->header.sync);
}

/*
 * Unlock synchronization mutex
 */
void client_unlock(const client_t client)
{
    if (client == NULL) return;
    sync_unlock(((ClientSocket)client)->header.sync);
}

/*
 * Return client user data ptr
 */
const void* client_get_data(const client_t client)
{
    return (client == NULL) ? NULL : ((ClientSocket)client)->header.data;
}

/*
 * Set client user data ptr
 */
void client_set_data(const client_t client, void* data)
{
    if (client != NULL) ((ClientSocket)client)->header.data = data;
}

/*
 * Return client port
 */
int client_get_port(const client_t client)
{
    return (client == NULL) ? 0 : ((ClientSocket)client)->header.port;
}

/*
 * Return client IP address
 */
char* client_get_addr(const client_t client)
{
    return (client == NULL) ? NULL : ((ClientSocket)client)->header.addr;
}

#ifdef __INSIGHT_OS_LINUX__
/*
 * Sleep for milliseconds
 */
int msleep(int msec)
{
    struct timespec tw;
    tw.tv_nsec = (msec % 1000) * 1000000;
    tw.tv_sec = msec / 1000;
    return nanosleep(&tw, NULL);
}
#endif

/*
 *  Timer thread
 */
static void* timer_func(void* arg)
{
    struct timeval tv;
    Timer t = (Timer)arg;
    while (1) {
        gettimeofday(&tv, NULL);
        uint64_t now, next, dt;
        now = (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
        next = now + t->timeout;
        next -= (next % t->timeout);
        dt = next - now;
        while (dt > 0 && msleep(dt) < 0)
        {
            switch (errno)
            {
            case EINTR:
                gettimeofday(&tv, NULL);
                now = (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
                dt = next - now;
                continue;
            default:
                pthread_exit(NULL);
            }
        }
        if (t->sync != NULL) sync_lock(t->sync);
        t->listener();
        if (t->sync != NULL) sync_unlock(t->sync);
    }
    return NULL;
}

/*
 * Create new timer
 * listener:    timer function
 * timeout:     timeout in milliseconds
 * Return timer ptr if success, otherwise NULL
 */
synctimer_t timer_new(timer_func_t listener, const int timeout, syncobj_t* sync)
{
    if (listener == NULL || timeout < T_QUANT)
    {
        errno = EINVAL;
        return NULL;
    }
    int t = (timeout / T_QUANT) * T_QUANT;
    Timer timer = (Timer)calloc(1, sizeof(struct Timer));

    if (NULL == timer)
    {
        return NULL;
    }

    timer->listener = listener;
    timer->timeout = t;
    timer->sync = sync;

    return timer;
}

/*
 * Start timer
 * Return 0 if success, otherwise -1
 */
int timer_start(synctimer_t timer)
{
    Timer t = (Timer)timer;
    if (t == NULL || t->listener == NULL)
    {
        errno = EINVAL;
        return -1;
    }

    if (THREAD(t->h_thread) != 0)
    {
        errno = EALREADY;
        return -1;
    }

    if (pthread_create(&t->h_thread, NULL, timer_func, t) != 0)
    {
        THREAD(t->h_thread) = 0;
        return -1;
    }

    return 0;
}

/*
 * Stop timer
 */
void timer_stop(synctimer_t timer)
{
    Timer t = (Timer)timer;
    if (t == NULL || t->listener == NULL || THREAD(t->h_thread) == 0) return;
    pthread_cancel(t->h_thread);
    pthread_join(t->h_thread, NULL);
    THREAD(t->h_thread) = 0;
}

/*
 * Delete timer
 */
void timer_free(synctimer_t timer)
{
    if (timer != NULL)
    {
        timer_stop(timer);
        free(timer);
    }
}

void timer_wait(synctimer_t timer)
{
    Timer t = (Timer)timer;
    if (t == NULL || t->listener == NULL || THREAD(t->h_thread) == 0) return;
    pthread_join(t->h_thread, NULL);
    THREAD(t->h_thread) = 0;
}

/*
 * SSL functions
 */
#ifdef __HAVE_SSL_SUPPORT__

static char keyf[256] = "Vulgaris.key";
static char certf[256] = "Vulgaris.crt";

void ssl_set_certificates(const char* keyfile, const char* certfile)
{
    if (keyfile != NULL)
    {
        strncpy(keyf, keyfile, sizeof(keyf));
    }

    if (certfile != NULL)
    {
        strncpy(certf, certfile, sizeof(certf));
    }
}

char* ssl_error(int errcode)
{
    static char buf[256];
    ERR_error_string_n(errcode, buf, sizeof(buf));
    return buf;
}

void ssl_init()
{
    ERR_load_crypto_strings();
    SSL_load_error_strings();
    SSLeay_add_ssl_algorithms();
}

void ssl_free()
{
    // Thread local cleanup
    ERR_remove_state(0);

    // Engine cleanup
    ENGINE_cleanup();
    CONF_modules_unload(1);

    EVP_cleanup();
    CRYPTO_cleanup_all_ex_data();
    ERR_free_strings();
}

static int ssl_server_init(ServerSocket s)
{
    SSL_CTX* ctx;
    int err;
    ctx = SSL_CTX_new(SSL_SERVER_METHOD());
    
    if (ctx == NULL)
    {
        errno = ERR_get_error();
        close_socket(&s->h_socket);
        return -1;
    }

    if (SSL_CTX_use_certificate_file(ctx, certf, SSL_FILETYPE_PEM) <= 0)
    {
        goto error1;
    }

    if (SSL_CTX_use_PrivateKey_file(ctx, keyf, SSL_FILETYPE_PEM) <= 0)
    {
        goto error1;
    }

    if (!SSL_CTX_check_private_key(ctx))
    {
        goto error1;
    }

    s->header.ctx = ctx;
    return 0;
error1:
    SSL_CTX_free(ctx);
    close_socket(&s->h_socket);
    err = ERR_get_error();
    if (err) errno = err;
    return -1;
}

static SSL* ssl_server_accept(ServerSocket s, int sock)
{
    SSL* ssl;
    X509* client_cert;
    char* str;
    int err;
    ssl = SSL_new(s->header.ctx);
    if (ssl == NULL) goto error1;
    SSL_set_fd(ssl, sock);
    if (SSL_accept(ssl) < 0) goto error;

    /* Get the cipher - optional */
    printf("SSL connection using %s\n", SSL_get_cipher(ssl));

    /* Get client's certificate (note: beware of dynamic allocation) - optional */
    client_cert = SSL_get_peer_certificate(ssl);
    if (client_cert != NULL) {
        printf("Client certificate:\n");
        str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0);
        if (str == NULL) goto error;
        printf("\t subject: %s\n", str);
        OPENSSL_free(str);
        str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0);
        if (str == NULL) goto error;
        printf("\t issuer: %s\n", str);
        OPENSSL_free(str);
        /* We could do all sorts of certificate verification stuff here before
          deallocating the certificate. */
        X509_free(client_cert);
    }
    else
        printf("Client does not have certificate.\n");
    return ssl;
error:
    SSL_free(ssl);
error1:
    err = ERR_get_error();
    if (err) errno = err;
    return NULL;
}

static int ssl_client_init(ClientSocket c)
{
    SSL_CTX* ctx;
    SSL* ssl;
    X509* server_cert;
    char* str;
    int err;
    ctx = SSL_CTX_new(SSL_CLIENT_METHOD());
    if (ctx == NULL) {
        close_socket(&c->connection.h_socket);
        return -1;
    }
    ssl = SSL_new(ctx);
    if (ssl == NULL) goto error1;
    SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
    SSL_set_fd(ssl, c->connection.h_socket);
    if (SSL_connect(ssl) < 0) goto error;

    /* Get the cipher - optional */
    printf("SSL connection using %s\n", SSL_get_cipher(ssl));

    /* Get server's certificate (note: beware of dynamic allocation) - optional */
    server_cert = SSL_get_peer_certificate(ssl);
    if (server_cert == NULL) goto error;
    printf("Server certificate:\n");

    str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0);
    if (str == NULL) goto error;
    printf("\t subject: %s\n", str);
    OPENSSL_free(str);
    str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0);
    if (str == NULL) goto error;
    printf("\t issuer: %s\n", str);
    OPENSSL_free(str);

    /* We could do all sorts of certificate verification stuff here before
     deallocating the certificate. */
    X509_free(server_cert);
    c->header.ctx = ctx;
    c->connection.ssl = ssl;
    return 0;
error:
    SSL_free(ssl);
error1:
    SSL_CTX_free(ctx);
    close_socket(&c->connection.h_socket);
    err = ERR_get_error();
    if (err) errno = err;
    return -1;
}
#endif


/*
****************************************************************************************************************
*= = 文件结束
****************************************************************************************************************
*/


